﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ObjectPool
{
    public class Pool<T> where T: class
    {
        private ArrayList pool; // Список объектов в пуле
        private int count;      // Текущее число объектов
        private int max;        // Максимальное число объектов в пуле 
        private Semaphore semaphore;    // Нужен для потокобезопасности
        private ICreator<T> creator;    // Интерфейсов объектов в пуле

        /// <summary>
        /// Длинна списка доступных объектов из пула
        /// </summary>
        public int Size
        {
            get { return pool.Count; }
        }

        /// <summary>
        /// Максимальное число объектов в пуле 
        /// </summary>
        public int Max
        {
            get { return max; }
            set { max = value; }
        }

        /// <summary>
        /// Текущее число объектов
        /// </summary>
        public int Count
        {
            get { return count; }
        }

        /// <summary>
        /// Конструктор пула
        /// </summary>
        /// <param name="creator">Об</param>
        /// <param name="maxCount"></param>
        public Pool(ICreator<T> creator, int maxCount)
        {
            this.creator = creator;
            max = maxCount;
            count = 0;
            pool = new ArrayList();
            semaphore = new Semaphore(0, max);
        }

        /// <summary>
        /// Создает новый экземпляр, если такого нет в списке объектов пула
        /// </summary>
        /// <returns></returns>
        private T CreateObject()
        {
            T newObject = creator.Create();
            count++;
            return newObject;
        }

        /// <summary>
        /// Получение объекта из пула
        /// </summary>
        /// <returns></returns>
        public T Take()
        {
            lock (pool)
            {
                T thisObject = RemoveObject();
                if (thisObject != null)
                    return thisObject;

                if (count < max)
                    return CreateObject();
            }
            semaphore.WaitOne();
            return Take();
        }

        /// <summary>
        /// Удаляет объект из списка объектов, находящихся в пуле
        /// </summary>
        /// <returns></returns>
        private T RemoveObject()
        {
            while (pool.Count > 0)
            {
                var refThis = (WeakReference)pool[pool.Count - 1];
                pool.RemoveAt(pool.Count - 1);
                var thisObject = (T)refThis.Target;
                if (thisObject != null)
                    return thisObject;
                count--;
            }
            return null;
        }

        /// <summary>
        /// Возвращает объект в пул объектов
        /// </summary>
        /// <param name="obj"></param>
        public void Put(T obj)
        {
            if (obj == null)
                throw new NullReferenceException();
            lock (pool)
            {
                var refThis = new WeakReference(obj);            
                pool.Add(refThis);
                semaphore.Release();
            }
        }
    }
}
